<template>
	<div
		class="el-slider"
		:class="{ 'is-vertical': vertical, 'el-slider--with-input': showInput }"
		role="slider"
		:aria-valuemin="min"
		:aria-valuemax="max"
		:aria-orientation="vertical ? 'vertical' : 'horizontal'"
		:aria-disabled="sliderDisabled"
	>
		<el-input-number
			v-model="firstValue"
			v-if="showInput && !range"
			class="el-slider__input"
			ref="input"
			@change="emitChange"
			:step="step"
			:disabled="sliderDisabled"
			:controls="showInputControls"
			:min="min"
			:max="max"
			:debounce="debounce"
			:size="inputSize"
		></el-input-number>
		<div class="el-slider__runway" :class="{ 'show-input': showInput, disabled: sliderDisabled }" :style="runwayStyle" @click="onSliderClick" ref="slider">
			<div class="el-slider__bar" :class='{ actual_runway: actual, target_runway: target}' :style="barStyle"></div>
			<slider-button :vertical="vertical" v-model="firstValue" :target="target" :actual="actual" :tooltip-class="tooltipClass" ref="button1"></slider-button>
			<slider-button :vertical="vertical" v-model="secondValue" :tooltip-class="tooltipClass" ref="button2" v-if="range"></slider-button>
			<div class="el-slider__stop" v-for="(item, key) in stops" :key="key" :style="getStopStyle(item)" v-if="showStops"></div>
			<template v-if="markList.length > 0">
				<div><div v-for="(item, key) in markList" :style="getStopStyle(item.position)" class="el-slider__stop el-slider__marks-stop" :key="key"></div></div>
				<div class="el-slider__marks">
					<slider-marker :mark="item.mark" v-for="(item, key) in markList" :key="key" :style="getStopStyle(item.position)"></slider-marker>
				</div>
			</template>
		</div>
	</div>
</template>

<script type="text/babel">
import ElInputNumber from 'element-ui/packages/input-number';
import SliderButton from './button.vue';
import SliderMarker from './marker';
import Emitter from 'element-ui/src/mixins/emitter';

export default {
	name: 'ElSlider',

	mixins: [Emitter],

	inject: {
		elForm: {
			default: ''
		}
	},

	props: {
		target: {
			type: Boolean,
			default: false
		},
		actual: {
			type: Boolean,
			default: false
		},
		min: {
			type: Number,
			default: 0
		},
		max: {
			type: Number,
			default: 100
		},
		step: {
			type: Number,
			default: 1
		},
		value: {
			type: [Number, Array],
			default: 0
		},
		showInput: {
			type: Boolean,
			default: false
		},
		showInputControls: {
			type: Boolean,
			default: true
		},
		inputSize: {
			type: String,
			default: 'small'
		},
		showStops: {
			type: Boolean,
			default: false
		},
		showTooltip: {
			type: Boolean,
			default: true
		},
		formatTooltip: Function,
		disabled: {
			type: Boolean,
			default: false
		},
		range: {
			type: Boolean,
			default: false
		},
		vertical: {
			type: Boolean,
			default: false
		},
		height: {
			type: String
		},
		debounce: {
			type: Number,
			default: 300
		},
		label: {
			type: String
		},
		tooltipClass: String,
		marks: Object
	},

	components: {
		ElInputNumber,
		SliderButton,
		SliderMarker
	},

	data() {
		return {
			firstValue: null,
			secondValue: null,
			oldValue: null,
			dragging: false,
			sliderSize: 1
		};
	},

	watch: {
		value(val, oldVal) {
			if (this.dragging || (Array.isArray(val) && Array.isArray(oldVal) && val.every((item, index) => item === oldVal[index]))) {
				return;
			}
			this.setValues();
		},

		dragging(val) {
			if (!val) {
				this.setValues();
			}
		},

		firstValue(val) {
			if (this.range) {
				this.$emit('input', [this.minValue, this.maxValue]);
			} else {
				this.$emit('input', val);
			}
		},

		secondValue() {
			if (this.range) {
				this.$emit('input', [this.minValue, this.maxValue]);
			}
		},

		min() {
			this.setValues();
		},

		max() {
			console.log(this.props);
			this.setValues();
		}
	},

	methods: {
		valueChanged() {
			if (this.range) {
				return ![this.minValue, this.maxValue].every((item, index) => item === this.oldValue[index]);
			} else {
				return this.value !== this.oldValue;
			}
		},
		setValues() {
			if (this.min > this.max) {
				console.error('[Element Error][Slider]min should not be greater than max.');
				return;
			}
			const val = this.value;
			if (this.range && Array.isArray(val)) {
				if (val[1] < this.min) {
					this.$emit('input', [this.min, this.min]);
				} else if (val[0] > this.max) {
					this.$emit('input', [this.max, this.max]);
				} else if (val[0] < this.min) {
					this.$emit('input', [this.min, val[1]]);
				} else if (val[1] > this.max) {
					this.$emit('input', [val[0], this.max]);
				} else {
					this.firstValue = val[0];
					this.secondValue = val[1];
					if (this.valueChanged()) {
						this.dispatch('ElFormItem', 'el.form.change', [this.minValue, this.maxValue]);
						this.oldValue = val.slice();
					}
				}
			} else if (!this.range && typeof val === 'number' && !isNaN(val)) {
				if (val < this.min) {
					this.$emit('input', this.min);
				} else if (val > this.max) {
					this.$emit('input', this.max);
				} else {
					this.firstValue = val;
					if (this.valueChanged()) {
						this.dispatch('ElFormItem', 'el.form.change', val);
						this.oldValue = val;
					}
				}
			}
		},

		setPosition(percent) {
			const targetValue = this.min + (percent * (this.max - this.min)) / 100;
			if (!this.range) {
				this.$refs.button1.setPosition(percent);
				return;
			}
			let button;
			if (Math.abs(this.minValue - targetValue) < Math.abs(this.maxValue - targetValue)) {
				button = this.firstValue < this.secondValue ? 'button1' : 'button2';
			} else {
				button = this.firstValue > this.secondValue ? 'button1' : 'button2';
			}
			this.$refs[button].setPosition(percent);
		},

		onSliderClick(event) {
			if (this.sliderDisabled || this.dragging) return;
			this.resetSize();
			if (this.vertical) {
				const sliderOffsetBottom = this.$refs.slider.getBoundingClientRect().bottom;
				this.setPosition(((sliderOffsetBottom - event.clientY) / this.sliderSize) * 100);
			} else {
				const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
				this.setPosition(((event.clientX - sliderOffsetLeft) / this.sliderSize) * 100);
			}
			this.emitChange();
		},

		resetSize() {
			if (this.$refs.slider) {
				this.sliderSize = this.$refs.slider[`client${this.vertical ? 'Height' : 'Width'}`];
			}
		},

		emitChange() {
			this.$nextTick(() => {
				this.$emit('change', this.range ? [this.minValue, this.maxValue] : this.value);
			});
		},

		getStopStyle(position) {
			return this.vertical ? { bottom: position + '%' } : { left: position + '%' };
		}
	},

	computed: {
		stops() {
			if (!this.showStops || this.min > this.max) return [];
			if (this.step === 0) {
				process.env.NODE_ENV !== 'production' && console.warn('[Element Warn][Slider]step should not be 0.');
				return [];
			}
			const stopCount = (this.max - this.min) / this.step;
			const stepWidth = (100 * this.step) / (this.max - this.min);
			const result = [];
			for (let i = 1; i < stopCount; i++) {
				result.push(i * stepWidth);
			}
			if (this.range) {
				return result.filter(step => {
					return step < (100 * (this.minValue - this.min)) / (this.max - this.min) || step > (100 * (this.maxValue - this.min)) / (this.max - this.min);
				});
			} else {
				return result.filter(step => step > (100 * (this.firstValue - this.min)) / (this.max - this.min));
			}
		},

		markList() {
			if (!this.marks) {
				return [];
			}

			const marksKeys = Object.keys(this.marks);
			return marksKeys
				.map(parseFloat)
				.sort((a, b) => a - b)
				.filter(point => point <= this.max && point >= this.min)
				.map(point => ({
					point,
					position: ((point - this.min) * 100) / (this.max - this.min),
					mark: this.marks[point]
				}));
		},

		minValue() {
			return Math.min(this.firstValue, this.secondValue);
		},

		maxValue() {
			return Math.max(this.firstValue, this.secondValue);
		},

		barSize() {
			return this.range ? `${(100 * (this.maxValue - this.minValue)) / (this.max - this.min)}%` : `${(100 * (this.firstValue - this.min)) / (this.max - this.min)}%`;
		},

		barStart() {
			return this.range ? `${(100 * (this.minValue - this.min)) / (this.max - this.min)}%` : '0%';
		},

		precision() {
			let precisions = [this.min, this.max, this.step].map(item => {
				let decimal = ('' + item).split('.')[1];
				return decimal ? decimal.length : 0;
			});
			return Math.max.apply(null, precisions);
		},

		runwayStyle() {
			return this.vertical ? { height: this.height } : {};
		},

		barStyle() {
			return this.vertical
				? {
						height: this.barSize,
						bottom: this.barStart
				  }
				: {
						width: this.barSize,
						left: this.barStart
				  };
		},

		sliderDisabled() {
			return this.disabled || (this.elForm || {}).disabled;
		}
	},

	mounted() {
		let valuetext;
		if (this.range) {
			if (Array.isArray(this.value)) {
				this.firstValue = Math.max(this.min, this.value[0]);
				this.secondValue = Math.min(this.max, this.value[1]);
			} else {
				this.firstValue = this.min;
				this.secondValue = this.max;
			}
			this.oldValue = [this.firstValue, this.secondValue];
			valuetext = `${this.firstValue}-${this.secondValue}`;
		} else {
			if (typeof this.value !== 'number' || isNaN(this.value)) {
				this.firstValue = this.min;
			} else {
				this.firstValue = Math.min(this.max, Math.max(this.min, this.value));
			}
			this.oldValue = this.firstValue;
			valuetext = this.firstValue;
		}
		this.$el.setAttribute('aria-valuetext', valuetext);

		// label screen reader
		this.$el.setAttribute('aria-label', this.label ? this.label : `slider between ${this.min} and ${this.max}`);

		this.resetSize();
		window.addEventListener('resize', this.resetSize);
	},

	beforeDestroy() {
		window.removeEventListener('resize', this.resetSize);
	}
};
</script>

<style scoped="scoped">
.actual_runway {
	background:  var(--custom-green);
}
</style>
